﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Security.Events.Commands;

namespace Security.Events
{
    /// <summary>
    /// Брокер событий. Отвечает выполнение команд <see cref="CommandBase"/>
    /// </summary>
    [Export]
    public class EventBroker : IObservable<CommandBase>
    {
        private readonly List<Subscription> subscribers = new List<Subscription>();

        /// <summary>
        /// Логгер для записи результатов выполнения команд
        /// </summary>
        [Import]
        public ILogger Logger { get; set; }

        /// <summary>
        /// Выполняет команду
        /// </summary>
        /// <typeparam name="T">Тип команды</typeparam>
        /// <param name="command">Команда</param>
        public void Publish<T>(T command) where T : CommandBase
        {
            foreach (var s in subscribers.ToArray())
                s.Subscriber.OnNext(command);

            WriteLog(command);
        }

        /// <summary>
        /// Подписывает метод на команду
        /// </summary>
        /// <param name="subscriber">Метод выполняющий команду</param>
        /// <returns></returns>
        public IDisposable Subscribe(IObserver<CommandBase> subscriber)
        {
            var sub = new Subscription(this, subscriber);
            if (!subscribers.Any(s => s.Subscriber == subscriber))
            {
                subscribers.Add(sub);
            }

            return sub;
        }

        /// <summary>
        /// Отписывает метод от команды
        /// </summary>
        /// <param name="subscriber">Метод</param>
        private void Unsubscribe(IObserver<CommandBase> subscriber)
        {
            subscribers.RemoveAll(s => s.Subscriber == subscriber);
        }

        /// <summary>
        /// Пишет в лог результыты выполнения команды
        /// </summary>
        /// <param name="command"></param>
        private void WriteLog(CommandBase command)
        {
            if (Logger != null)
            {
                Logger.WriteLog(command.ToString());
            }
        }

        private class Subscription : IDisposable
        {
            private readonly EventBroker broker;

            public IObserver<CommandBase> Subscriber { get; private set; }

            public Subscription(EventBroker broker, IObserver<CommandBase> subscriber)
            {
                this.broker = broker;
                this.Subscriber = subscriber;
            }

            public void Dispose()
            {
                broker.Unsubscribe(Subscriber);
            }
        }
    }
}
